package me.qi.kancha.configuration;

import com.google.gson.*;
import com.google.gson.annotations.Expose;
import com.google.gson.reflect.TypeToken;
import me.qi.kancha.dto.enums.base.BaseEnum;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.GsonHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * web mvc 配置：
 * gson转换器
 *
 * @author sun
 * @date 2019/5/29 15:05
 */
@Configuration
@EnableWebMvc
public class WebMvcConfiguration implements WebMvcConfigurer {

    @Value("${spring.gson.date-format}")
    private String dateFormat;

    // @Value("${file.dir}")
    // private String dir;

    private Map<String,Object> map = new HashMap<>();

    /**
     * 使用了Expose注解并值为false的字段忽略
     */
    private ExclusionStrategy exclusionStrategy = new ExclusionStrategy() {
        @Override
        public boolean shouldSkipField(FieldAttributes f) {
            Expose expose = f.getAnnotation(Expose.class);
            if (expose != null && !expose.serialize()) {
                return true;
            }
            return false;
        }

        @Override
        public boolean shouldSkipClass(Class<?> clazz) {
            return false;
        }
    };

    /**
     * 自定义类型适配
     */
    private TypeAdapterFactory typeAdapterFactory = new TypeAdapterFactory() {
        @Override
        public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
            Class<T> rawType = (Class<T>) type.getRawType();
            if (rawType == String.class) {
                return GsonTypeAdapter.stringTypeAdapter;
            }
            if (OAuth2Exception.class.isAssignableFrom(rawType)) {
                return GsonTypeAdapter.oauthExceptionAdapter;
            }

            if (BaseEnum.class.isAssignableFrom(rawType)) {
                if (map.keySet().contains(rawType.getName())) {
                    return (TypeAdapter<T>) map.get(rawType.getName());
                } else {
                    GsonTypeAdapter.EnumAdapter enumAdapter = new GsonTypeAdapter.EnumAdapter(rawType);
                    map.put(rawType.getName(), enumAdapter);
                    return (TypeAdapter<T>) enumAdapter;
                }
            }
            return null;
        }
    };

    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        //构建gson转换器
        Gson gson = new GsonBuilder()
                .setDateFormat(dateFormat)
                .serializeNulls()
                .registerTypeAdapterFactory(typeAdapterFactory)
                .addSerializationExclusionStrategy(exclusionStrategy)
                //日期时间序列化&反序列化
                .registerTypeAdapter(LocalDate.class, GsonTypeAdapter.dateSerializer)
                .registerTypeAdapter(LocalDate.class, GsonTypeAdapter.dateDeserializer)
                .registerTypeAdapter(LocalDateTime.class, GsonTypeAdapter.dateTimeSerializer)
                .registerTypeAdapter(LocalDateTime.class, GsonTypeAdapter.dateTimeDeserializer)
                .create();
        GsonHttpMessageConverter converter = new GsonHttpMessageConverter();
        converter.setGson(gson);
        //删除MappingJackson2HttpMessageConverter
        converters.removeIf(httpMessageConverter -> httpMessageConverter instanceof MappingJackson2HttpMessageConverter);
        //加入gson 转换器
        converters.add(converter);
    }

    // @Override
    // public void addResourceHandlers(ResourceHandlerRegistry registry) {
    //     registry.addResourceHandler("/view/**").addResourceLocations(String.format("file:%s/", dir));
    // }
}
